﻿using Smile.Network.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
#if NETFX_CORE
using Windows.Security.Authentication.Web;
#endif

namespace Smile.Network.OAuth
{
    public abstract class OAuthClientBase : IOAuthClient
    {
        // @Properties
        public string RequestTokenUrl
        {
            get
            {
                return _requestTokenUrl;
            }
        }

        public string AccessTokenUrl
        {
            get
            {
                return _accessTokenUrl;
            }
        }

        public string AuthorizeUrl
        {
            get
            {
                return _authorizeUrl;
            }
        }

        public string AuthorizeCallbackUrl
        {
            get
            {
                return _authorizeCallbackUrl;
            }
            protected set
            {
                _authorizeCallbackUrl = value;
            }
        }

        public string ConsumerKey
        {
            get
            {
                return _consumerKey;
            }
            set
            {
                _consumerKey = value;
                IsAuthenticated = false;
            }
        }

        public string ConsumerSecret
        {
            get
            {
                return _consumerSecret;
            }
            set
            {
                _consumerSecret = value;
                IsAuthenticated = false;
            }
        }

        public string Token
        {
            get
            {
                return _token;
            }
            set
            {
                _token = value;
                IsAuthenticated = false;
            }
        }

        public string TokenSecret
        {
            get
            {
                return _tokenSecret;
            }
            protected set
            {
                _tokenSecret = value;
            }
        }

        public string Verifier
        {
            get
            {
                return _verifier;
            }
            protected set
            {
                _verifier = value;
            }
        }

        public string UserId
        {
            get
            {
                return _userId;
            }
            set
            {
                _userId = value;
            }
        }

        public bool IsAuthenticated
        {
            get
            {
                return _isAuthenticated;
            }
            protected set
            {
                _isAuthenticated = value;
            }
        }

        public bool CallbackConfirmed
        {
            get
            {
                return _callbackConfirmed;
            }
            protected set
            {
                _callbackConfirmed = value;
            }
        }

        public DateTime Expires
        {
            get
            {
                return _expires;
            }
            protected set
            {
                _expires = value;
            }
        }


        // @Public
        public OAuthClientBase(string requestTokenUrl, string authorizeUrl, string accessTokenUrl)
        {
            _requestTokenUrl = requestTokenUrl;
            _accessTokenUrl = accessTokenUrl;
            _authorizeUrl = authorizeUrl;
        }

        public async Task<bool> AuthenticateAsync(string consumerKey, string consumerSecret, string authorizeCallbackUrl)
        {
            // Initialize data.
            ConsumerKey = consumerKey;
            ConsumerSecret = consumerSecret;
            AuthorizeCallbackUrl = authorizeCallbackUrl;
            Token = "";
            TokenSecret = "";
            Verifier = "";

            // Request token
            if (await ProcessRequestTokenStepAsync())
            {
                // Authorize code
                if (await ProcessAuthorizeStepAsync())
                {
                    // Access token
                    return await ProcessAccessStepToken();
                }
            }
            return false;
        }

        public Task<bool> AuthenticateAsync(string consumerKey, string authorizeCallbackUrl)
        {
            ConsumerKey = consumerKey;
            AuthorizeCallbackUrl = authorizeCallbackUrl;
            throw new NotImplementedException();
        }


        // @Protected
        protected readonly string   _requestTokenUrl;
        protected readonly string   _accessTokenUrl;
        protected readonly string   _authorizeUrl;
        protected string            _authorizeCallbackUrl;
        protected string            _consumerKey = "";
        protected string            _consumerSecret = "";
        protected string            _token = "";
        protected string            _userId = "";
        protected string            _tokenSecret = "";
        protected string            _verifier = "";
        protected bool              _isAuthenticated = false;
        protected bool              _callbackConfirmed = false;
        protected DateTime          _expires = DateTime.Now;

        protected abstract string GenerateBaseString(string targetUrl, UriArgumentsDictionary parameters, string method = "POST");
        protected abstract string GenerateSignature(string baseString = "");
        protected abstract UriArgumentsDictionary GenerateRequestTokenParameters();
        protected abstract UriArgumentsDictionary GenerateAccessTokenParameters();

        protected async Task<bool> ProcessRequestTokenStepAsync()
        {
            // Request servers.
            var request = new HttpRequest();
            var response = await request.PostAsync(
                    RequestTokenUrl,
                    GenerateRequestTokenParameters()
                );

            // Parsing response
            if (response.Status == HttpStatusCode.OK)
            {
                var responseParam = response
                    .Content
                    .Split('&');
                for (int i = 0; i < responseParam.Length; ++i)
                {
                    var responseValues = responseParam[i].Split('=');
                    switch (responseValues[0])
                    {
                        case "oauth_token":
                            Token = responseValues[1];
                            break;
                        case "oauth_token_secret":
                            TokenSecret = responseValues[1];
                            break;
                        case "oauth_callback_confirmed":
                            CallbackConfirmed = (responseValues[1].ToLower() == "true");
                            break;
                    }
                }
                return CallbackConfirmed;
            }
            return false;
        }

        protected async Task<bool> ProcessAuthorizeStepAsync()
        {
            try
            {
                var wabr = await WebAuthenticationBroker.AuthenticateAsync(
                    WebAuthenticationOptions.None,
                    new Uri(AuthorizeUrl + "?oauth_token=" + Token),
                    new Uri(AuthorizeCallbackUrl));
                if (wabr.ResponseStatus == WebAuthenticationStatus.Success)
                {
                    // Parsing response
                    var responseParam = wabr
                        .ResponseData
                        .Split('&');
                    for (int i = 0; i < responseParam.Length; ++i)
                    {
                        var responseValues = responseParam[i].Split('=');
                        switch (responseValues[0])
                        {
                            case "oauth_token":
                                if (responseValues[1] != Token)
                                {
                                    return false;
                                }
                                break;
                            case "oauth_verifier":
                                Verifier = responseValues[1];
                                break;
                        }
                    }
                    return true;
                }
            }
            catch (Exception)
            {
            }
            return false;
        }

        protected async Task<bool> ProcessAccessStepToken()
        {
            // Request servers.
            var request = new HttpRequest();
            var response = await request.PostAsync(
                    AccessTokenUrl, 
                    GenerateAccessTokenParameters()
                );

            // Parsing response.
            if (response.Status == HttpStatusCode.OK)
            {
                var responseParam = response
                    .Content
                    .Split('&');
                for (int i = 0; i < responseParam.Length; ++i)
                {
                    var responseValues = responseParam[i].Split('=');
                    switch (responseValues[0])
                    {
                        case "oauth_token":
                            Token = responseValues[1];
                            break;
                        case "oauth_token_secret":
                            TokenSecret = responseValues[1];
                            break;
                        case "user_id":
                            UserId = responseValues[1];
                            break;
                    }
                }
                return true;
            }

            return false;
        }

        protected string GenerateNonce()
        {
            var rand = new Random();
            return rand
                .Next(1000000000)
                .ToString();
        }
    }
}
